package lime.middleware;

/**
 * <p>Title: Lime</p>
 * <p>Description: Lime</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: DII - DISMI</p>
 * @author Marco Mamei
 * @version 1.0
 */

import java.util.*;

import emulator.utils.*;
import tuplespace.tuples.*;
import eventengine.middleware.*;
import corepeerservices.middleware.*;
import corepeerservices.tuples.*;
import lime.tuples.*;

/* the lime network engine is the component in charge of monitoring other
peers' connections and disconnections and transfer and remove tuples from
the virtual tuple space accordingly. */

public class LimeNetworkEngine implements ReactiveComponent
{
 private Lime lime;
 private CoreMiddleware core;
 private EventInterface networkEvent;

 public LimeNetworkEngine(Lime lime, CoreMiddleware core)
 {
  this.lime = lime;
  this.core = core;
  networkEvent = (EventInterface)new EventServer("LNE"+lime.peer.toString());
  core.setOutsideEvent(networkEvent);

  /* set up call back reading */
  lime.peer.callBackReading(lime.LIME_SOCKET,"<LIME><"+lime.peer.toString()+"><PC>",this,"LIMEPC");

  /* subscribe to network events */
  NetworkTuple pc = new NetworkTuple("<op=PC><ip=*>");
  networkEvent.subscribe(pc,this,"PC");
  NetworkTuple pd = new NetworkTuple("<op=PD><ip=*>");
  networkEvent.subscribe(pd,this,"PD");
 }


 /*****************************************************************************/
 /*                          REACTIVE INTERFACE                               */
 /*****************************************************************************/
 public void react(String reaction, String event)
 {
  if(reaction.equalsIgnoreCase("PC"))
  {
   NetworkTuple nt = (NetworkTuple)Tuple.deserialize(event);
   String peerName = emulator.utils.StaticUtilities.getPeerName(nt.ip);
   /* first insert a presence tuple in the virtual tuple space */
   PresenceTuple pt = new PresenceTuple("<peer="+peerName+">");
   lime.vts.write(pt);
   /* second send my tuples to the new neighbor peer */
   String reply = "<LIME><"+peerName+"><PC>";
   /* get my tuples */
   Tuple all = new Tuple("<content=*>");
   Vector v = lime.ts.read(all);
   /* compose message */
   for(int i=0;i<v.size();i++)
   {
    Tuple t = (Tuple)v.get(i);
    String flatT = t.serialize();
    
    System.err.println("from "+lime.peer.getAddress()+" to "+peerName+": "+flatT);
    
    if(!lime.isInBlackList(flatT))
     reply = reply +"<"+flatT+">";
   }

   if(reply.length() > ("<LIME><"+peerName+"><PC>").length())
    lime.peer.send(lime.LIME_SOCKET,reply); // send the message
                                            // only if tuples are actually sent
  }

  else if(reaction.equalsIgnoreCase("PD"))
  {
   NetworkTuple nt = (NetworkTuple)Tuple.deserialize(event);
   String peerName = emulator.utils.StaticUtilities.getPeerName(nt.ip);
   lime.vts.removePeer(peerName);
  }
  if(reaction.equalsIgnoreCase("LIMEPC"))
  {
   //System.err.println("*** "+lime.peer.getAddress()+": "+event);
   /* here it comes "<LIME><"+peerName+"><PC><t1><t2>...<tn>" */
   String[] data = emulator.utils.StaticUtilities.splitMessage(event,'<','>');
   Tuple sample = Tuple.deserialize(data[3]);
   if(lime.vts.keykeyrd(sample) != null)
   {
    /* I already know this stuff! */
    /* However I have to take an extra step, to deal with the problem of
    asymmetric links: if I receive tuples from a node that I think is
    already a neighbor of mine, I have to fire back by tuples, since I
    think that the node is a neighbor, but it is likely not to think at
    me as a neighbor! */
    /* let's fake a connetion */
    String ip = StaticUtilities.getPeerAddress(data[2]);
    NetworkTuple nt = new NetworkTuple("<op=PC><ip="+ip+">");
    if(!(lime.peer instanceof emulator.peers.Peer))
     react("PC",nt.serialize());
   }
   else
   {
    /* this is new stuff! */
    for(int i=3;i<data.length;i++)
    {
     Tuple t = Tuple.deserialize(data[i]);
     lime.vts.write(t);
    }
   }
  }
 }

 /*****************************************************************************/
 /*                              STEP METHOD                                  */
 /*****************************************************************************/
 public void step(int time)
 {
  core.step(time);
 }
}